/**
 * Created by Administrator on 2018-5-9.
 */
(()=>{
	window.Collector = class Collector extends Data {
		static get model() {return null};
		static get tableAlias() {return 'main'};
		
		static get EQ() {return "="};
		static get NEQ() {return "!="};
		static get LEQ() {return "<="};
		static get REQ() {return ">="};
		static get LT() {return "<"};
		static get RT() {return ">"};
		static get LIKE() {return "LIKE"};
		
		static get OR() {return "OR"};
		static get AND() {return "AND"};
		
		static getAll() {
			return (new this()).sort('priority', 'ASC').sort('id', 'ASC').load().get();
		}
		
		constructor(noselect) {
			super();
			
			this._columns = [];
			this._where = [];
			this._sort = [];
			this._page = 1;
			this._rowSize = 0;
			this._group = [];
			this._having = [];
			this._join = [];
			this._data = [];
			
			this._lastJoin = null;
			
			this._isLoad = false;
			this._lastError = null;
			
			this._aryData = [];
			
			this.setTable(this.constructor.model.table);
			
			if(!noselect) {
				this.select('*');
			}
		}
		
		get table() {
			return this.getTable();
		}
		
		set table(table) {
			return this.setTable(table);
		}
		
		setTable(table) {
			this._table = table;
			return this;
		}
		
		getTable() {
			return this._table;
		}
		
		get() {
			return this._data;
		}
		
		getData() {
			return this._aryData;
		}
		
		set(data) {
			this._data = data;
			return this;
		}
		
		getSql() {
			let conditionStr = '';
			for(let i=0; i<this._where.length; i++) {
				conditionStr += this._processingCondition(this._where[i]);
			}
			
			let joinstr = '';
			for(let i=0; i<this._join.length; i++) {
				joinstr += this._processingJoin(this._join[i]);
				
				if(this._join[i].columns) {
					this.select(this._join[i].columns);
				}
			}

			let havingStr = '';
			for(let i=0; i<this._having.length; i++) {
				havingStr += this._processingCondition(this._having[i]);
			}

			let th = this;
			let groupbyStr = this._group.map((row)=>{return th._processingField(row)}).join(',');
			
			let orderbyStr = this._sort.map((row)=> {
				return th._processingField(row.field) +  (row.direction ? ' '+row.direction : '');
			}).join(',');
			
			let limitStr = Number(this._rowSize) ? String(this._rowSize) : '';
			
			if(limitStr != '') {
				limitStr += ' OFFSTE ' + String((this._page-1)*Number(this._rowSize));
			}
			
			return "SELECT " +
				this._columns.join(',') + ' ' +
				'FROM ' + _processingField(this.table) + ` AS "${this.constructor.tableAlias}" ` +
				joinstr +
				(conditionStr ? (" WHERE " + conditionStr) : '') + //where
				(havingStr ? (" HAVING " + havingStr) : '') + //having
				(groupbyStr ? (" GROUP BY " + groupbyStr) : '') + //groupby
				(orderbyStr ? (" ORDER BY " + orderbyStr) : '') + //orderby
				(limitStr ? (" LIMIT " + limitStr) : '') + //limit
				""
			;
		}
		
		where(field, value, operator) {
			return this._whereCondition(field, value, operator);
		}
		
		orWwhere(field, value, operator) {
			return this._whereCondition(field, value, operator, this.constructor.OR);
		}
		
		_whereCondition(field, value, operator, cond, saveObj) {
			if(!operator) {
				operator = this.constructor.EQ;
			}
			
			if(!saveObj) {
				saveObj = this._where;
			}
			
			if(!cond) {
				cond = this.constructor.AND;
			}
			
			if(saveObj.length > 0) {
				saveObj.push(cond);
			}
			
			if(Core.isArray(field)) {
				saveObj.push(field);
			} else if(field.toSqlString) {
				saveObj.push(field);
			} else {
				saveObj.push(this.constructor.cond(field, value, operator));
			}
			
			return this;
		}
		
		rowSize(size) {
			this._rowSize = size;
			return this;
		}
		
		page(page) {
			this._page = page;
			return this;
		}

		/**
		 * 排序, direction 为false则删除对应的field排序
		 * @param field
		 * @param direction
		 * @returns {Collector}
		 */
		sort(field, direction) {
			field = field.toUpperCase();
			direction = (direction!==false && direction) ? direction : 'ASC';
			
			for(let i=0; i<this._sort.length; i++) {
				if(this._sort[i].field == field) {
					if(direction === false) {
						this._sort.splice(i, 1);
						return this;
					} else {
						this._sort[i].direction = direction;
						return this;
					}
				}
			}
			
			if(direction !== false) {
				this._sort.push({field:field, direction:direction});
			}
			
			return this;
		}
		
		sortDesc(field) {
			return this.sort(field, 'DESC');
		}
		
		sortAsc(field) {
			return this.sort(field, 'ASC');
		}
		
		groupBy(field) {
			if(Core.isArray(field)) {
				for(let i=0; i<field.length; i++) {
					this.groupBy(field[i]);
				}
			} else {
				if(field == '') {
					return this;
				}
				
				if(this._group.indexOf(field) != -1) {
					return this;
				}

				this._group.push(field);
			}
			
			return this;
		}

		/**
		 * 移除某个groupby
		 * @param field
		 * @returns {Collector}
		 */
		removeGroupBy(field) {
			if(Core.isArray(field)) {
				for(let i=0; i<field.length; i++) {
					this.removeGroupBy(field[i]);
				}
			} else {
				let pos = this._group.indexOf(field);
				if(pos != -1) {
					this._group.splice(pos, 1);
				}
			}

			return this;
		}

		/**
		 * having
		 * @param field
		 * @param value
		 * @param operator
		 * @returns {*}
		 */
		having(field, value, operator) {
			return this._whereCondition(field, value, operator, this.constructor.AND, this._having);
		}

		orHaving(field, value, operator) {
			return this._whereCondition(field, value, operator, this.constructor.OR, this._having);
		}
		
		join(table, columns, on, joinType) {
			if(!joinType) {
				joinType = 'INNER JOIN';
			}
			
			if(columns && Core.isArray(columns)) {
				for(let i=0; i<columns.length; i++) {
					if(columns[i].indexOf('.') == -1) {
						columns[i] = (table._alias ? table._alias : table) + '.' + columns[i]
					}
				}
			}
			
			this._join.push({table:table, on:on, columns:columns, type:joinType});
			this._lastJoin = this._join[this._join.length-1];
			return this;
		}
		
		on(field, value, operator) {
			if(this._lastJoin) {
				if(!Core.isArray(this._lastJoin.on)) {
					this._lastJoin.on = [];
				}
				
				this._whereCondition(field, value, operator, this.constructor.AND, this._lastJoin.on);
			}
			
			return this;
		}
		
		orOn(field, value, operator) {
			if(this._lastJoin) {
				this._whereCondition(field, value, operator, this.constructor.OR, this._lastJoin.on);
			}

			return this;
		}
		
		joinCross(table, columns, on) {
			return this.join(table, columns, on, "CROSS JOIN");
		}

		joinOuter(table, columns, on) {
			return this.join(table, columns, on, "OUTER JOIN");
		}
		
		/**
		 * 选择field
		 * @param columns
		 * @returns {Window.Collector}
		 */
		select(columns) {
			if(arguments.length > 1) {
				for(let i=0; i<arguments.length; i++) {
					this.select(arguments[i]);
				}
			} else {
				if(Core.isArray(columns)) {
					for(let i=0; i<columns.length; i++) {
						if(Core.isString(columns[i])) {
							if(columns[i].indexOf('.') == -1) {
								columns[i] = this.constructor.tableAlias + '.' + columns[i];
							}
						}
						
						let columnString = this._processingField(columns[i]);
						
						if(this._columns.indexOf(columnString) != -1) {
							continue;
						}
						
						this._columns.push(columnString);
					}
				} else {
					this.select([columns]);
				}
			}
			
			return this;
		}
		
		load() {
			if(this._isload) {
				return this;
			}
			
			let result = Model.run(this.getSql());

			if(!result.error) {
				let objData = [];
				this._aryData = result.data;
				this._ok = true;
				this._isload = true;
				this._lastError = null;
				
				for(let i=0; i<result.data.length; i++) {
					objData.push((new this.constructor.model()).set(result.data[i]));
				}
				
				this._data = objData;
			} else {
				this._ok = false;
				this._lastError = result.error;
			}

			return this;
		}
		
		ok() {
			return this._ok;
		}
		
		static raw(content) {
			return new _Raw(content);
		}
		
		static alias(field, alias) {
			return new _Alias(field, alias);
		}
		
		static isNUll(field) {
			return new _IsNull(field);
		}

		static col(field) {
			return new _Col(field);
		}
		
		static cond(field, value, cond) {
			if(!cond) {
				cond = this.EQ;
			}

			return new _Col(field).expr(cond, value);
		};
		
		_processingField(row) {
			return _processingField(this._addBeyound(row));
		}

		_processingCondition(conds) {
			let str = '';

			if(Core.isArray(conds)) {
				for(let i=0; i<conds.length; i++) {
					let result = this._processingCondition(conds[i]);

					if(Core.isArray(conds[i]) && conds[i].length > 1) {
						result = `(${result.replace(/\s+$/g,"")}) `;
					}

					str += result;
				}

				return str;
			}
			this._addBeyound(conds);
			return  (conds.toSqlString ? conds.toSqlString() : conds) + ' ';
		}
		
		_addBeyound(obj) {
			if(obj instanceof _Col) {
				obj.beyound = this.constructor.tableAlias;
			}
			
			return obj;
		}

		_processingJoin(join) {
			return join.type + ' ' + this._processingField(join.table) +
				(join.on ? (' ON ' + this._processingCondition(join.on)) : '');
		}
	};
	
	function _processingField(field) {
		return field.toSqlString ? field.toSqlString() : _escapeField(field);
	}
	
	function _escapeField(fieldName) {
		let ary =fieldName.split('.');
		
		for(let i=0; i<ary.length; i++) {
			ary[i] = ary[i] == '*' ? ary[i] : `"${ary[i]}"`;
		}
		
		return ary.join('.');
	}
	
	function _escapeValue(value) {
		if(Core.isString(value)) {
			value = Model.escapeValue(value);
			value = `'${value}'`;
		}
		
		if(Core.isNull(value)) {
			return 'null';
		}

		if(Core.isUndefined(value)) {
			return '';
		}

		if(value instanceof _Col) {
			value.beyound = this.constructor.tableAlias;
		}
		
		return value.toSqlString ? value.toSqlString() : value;
	}

	class _Raw {
		constructor(content) {
			this._content = content;
		}
		
		toString() {
			return this._content;
		}
	}
	
	class _Alias {
		constructor(field, alias) {
			this._field = field;
			this._alias = alias;
		}
		
		toSqlString() {
			return _escapeField(this._field) + ` AS "${this._alias}"`;
		}
	}
	
	class _IsNull {
		constructor(field) {
			this._field = field;
		}
		
		toSqlString() {
			return `${this._field} IS NULL`;
		}
	}
	
	window.AND = Collector.AND;
	window.OR = Collector.OR;
	window.RAW = Collector.raw;
	window.ALIAS = Collector.alias;
	window.COL = Collector.col;
	
	Core.setToParentWindow('COL', COL);
	Core.setToParentWindow('ALIAS', ALIAS);
	Core.setToParentWindow('RAW', RAW);
	Core.setToParentWindow('OR', OR);
	Core.setToParentWindow('AND', AND);
	
	class _Col {
		constructor(column, isValue) {
			if(isValue) {
				this._statement = _escapeValue(column);
			} else {
				this._statement = _processingField(column);
			}
			
			this._column = column;
			this._right = '';
			this.multipleExpression = false;
		}
		
		expr(operator, value) {
			if (this.multipleExpression) {
				this._statement = `(${this._statement})` + operator + _escapeValue(value);
			} else {
				this._statement += operator + _escapeValue(value);
			}

			this.multipleExpression = true;
			return this;
		}

		/**
		 * 加
		 * @param num
		 * @returns {_Col}
		 */
		plus(num) {
			if(!Core.isNumber(num)) {
				num = 1;
			}
			
			if(this.multipleExpression) {
				this._statement = `(${this._statement})` + (num > 0 ? `+${num}`:`-${num}`);
			} else {
				this._statement += (num > 0 ? `+${num}`:`-${num}`);
			}
			
			this.multipleExpression = true;
			return this;
		}
		
		minus(num) {
			return this.plus(-num);
		}

		multiplyBy(num) {
			if(!Core.isNumber(num)) {
				return this;
			}

			if(this.multipleExpression) {
				this._statement = `(${this._statement})` + `*${num}`;
			} else {
				this._statement += `*${num}`;
			}
			
			this.multipleExpression = true;
			return this;
		}

		multiplyBy(num) {
			if(!Core.isNumber(num)) {
				return this;
			}

			if(this.multipleExpression) {
				this._statement = `(${this._statement})` + `*${num}`;
			} else {
				this._statement += `*${num}`;
			}

			this.multipleExpression = true;
			return this;
		}

		divide(num) {
			if(!Core.isNumber(num)) {
				return this;
			}

			if(this.multipleExpression) {
				this._statement = `(${this._statement})` + `/${num}`;
			} else {
				this._statement += `/${num}`;
			}

			this.multipleExpression = true;
			return this;
		}
		
		count() {
			this._statement = `COUNT(${this._statement})`;
			return this;
		}

		sum() {
			this._statement = `sum(${this._statement})`;
			return this;
		}

		concat (str) {
			if (this.multipleExpression) {
				this._statement = `(${this._statement})` + ' || ' + _escapeValue(str);
			} else {
				this._statement += ' || ' + _escapeValue(str);
			}

			this.multipleExpression = true;
			return this;
		}
		
		as(name) {this._as = name;return this;}
		distinct () {this._statement = 'DISTINCT ' + this._statement;return this;}
		
		eq(str) {this._right = '=' + _escapeValue(str);return this;}
		neq(str) {this._right = '!=' + _escapeValue(str);return this;}
		lt(str) {this._right = '<' + _escapeValue(str);return this;}
		leq(str) {this._right = '<=' + _escapeValue(str);return this;}
		rt(str) {this._right = '>' + _escapeValue(str);return this;}
		req(str) {this._right = '>=' + _escapeValue(str);return this;}
		mod(str) {this._right = '%' + _escapeValue(str);return this;}
		like(str) {this._right = ' LIKE ' + _escapeValue(str);return this;}
		inData(str) {this._right = ' IN ' + _escapeValue(str);return this;}
		notInData(str) {this._right = ' NOT IN ' + _escapeValue(str);return this;}
		glob(str) {this._right = ' GLOB ' + _escapeValue(str);return this;}
		
		isNull() {this._right = ' IS NULL';return this;}
		isNotNull() {this._right = ' IS NOT NULL';return this;}
		
		toSqlString() {
			let str = '';
			
			if(this._column.indexOf('.') == -1) {
				str = this.beyound ? `"${this.beyound}".` : '';
			}
			return str + String(this._statement)+String(this._right) + (this._as ? ' AS ' + _processingField(this._as) : '');
		}
	}
	
	Core.setToParentWindow('Collector', Collector);
})();

// class TestCollector extends Collector{
// 	static get model() {return TestModel;};
// }
//
// let a = new TestCollector;
// a.select(COL('AA').as('A'), 'a4', 'a5')
// 	.join('BB', ['b1'])
// 	.on('b1', COL('a1'))
// 	.where([
// 		COL('a2').eq(29), AND, COL('a3').eq("Let's go"), OR,
// 		[ COL('a4').eq(10), OR, COL('b2').rt(18) ]
// 	])
// 	.orWwhere([ COL('A.aa12').like('%sss%'), OR, COL('A.total').count().eq(10) ])
// 	.sort('A.a5')
// 	.sort('A.a4', 'DESC')
// 	.groupBy('A.A')
// 	.having('BB.b2', 'hahaha')
// 	.rowSize(20)
// 	.page(3)
// ;
// console.log(a.getSql());
// console.log(a);